﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UofCASE.AgileProductLinerDSL.Confeaturator;

namespace UofCASE.AgileProductLinerDSL.Confeaturator.Model
{
    public class FeatureNode
    {

        public IList<FeatureNode> required{ get; private set; }   // includes parent
        public IList<FeatureNode> requiredBy{ get; private set; } // includes children
        
        public IList<FeatureNode> excludedBy{ get; private set; }
        public IList<FeatureNode> excludes{ get; private set; }


        public FeatureModelElement FeatureModelElement
        {
            get;
            set;
        }
        public UpdateableUI UINode {get; set;}
        public FeatureModelNodeKind Kind
        {
            get;
            set;
        }
        public FeatureModelNodeStatus Status { 
            get{
                if (Kind == FeatureModelNodeKind.Alternative) return FeatureModelNodeStatus.Alternative;
                if (IsChecked)
                {
                    if (locked())
                    {
                        return FeatureModelNodeStatus.CheckedAndDisabled;
                    }
                    else
                    {
                        return FeatureModelNodeStatus.CheckedAndEnabled;
                    }
                }
                else
                {
                    if (locked())
                    {
                        return FeatureModelNodeStatus.UncheckedAndEnabled; 
                        // FeatureModelNodeStatus.UncheckedAndDisabled;
                    }
                    else
                    {
                        return FeatureModelNodeStatus.UncheckedAndEnabled;
                    }
                }
            }
        }
        bool checkd;

        public FeatureNode(FeatureModelElement feature)
        {
            if (feature == null)
            {
                throw new ArgumentException("Cannot build Confeaturator tree with null features");
            }
            this.FeatureModelElement = feature;
            Alternative alternative = feature as Alternative;
            if (alternative != null)
            {
                this.Kind = FeatureModelNodeKind.Alternative;
            }
            else
            {
                Feature asfeature = feature as Feature;
                switch (asfeature.Occurence)
                {
                    case Occurence.Mandatory:
                        this.Kind = FeatureModelNodeKind.Mandatory;
                        this.checkd = true;
                        break;
                    case Occurence.Optional:
                        this.Kind = FeatureModelNodeKind.Optional;
                        break;
                    case Occurence.NotApply:
                        this.Kind = FeatureModelNodeKind.NotApply;
                        break;
                    default:
                        break;
                }
            }
            required = new List<FeatureNode>();
            requiredBy = new List<FeatureNode>();
            excludedBy = new List<FeatureNode>();
            excludes = new List<FeatureNode>();
        }

        public void Check()
        {
            if (!locked())
            {
                checkd = true;
                foreach (FeatureNode node in required)
                    node.Check();
                notifyUI();
            }
        }

        public void UnCheck()
        {
            if (!locked())
            {
                checkd = false;
                foreach (FeatureNode node in requiredBy)
                    node.UnCheck();
                notifyUI();
            }
        }

        public bool IsChecked{
            get {
                if (Kind == FeatureModelNodeKind.Alternative)
                    checkd = required[0].IsChecked;
                return checkd; 
            }
        }

        public bool locked()
        {
            // locked if 
            //  any requirment is unsatisfied
            //  any selected thing would be exluded
            //  any thing is excluding it
            //  any thing that is selected would be excluded by this
            if (Kind == FeatureModelNodeKind.Mandatory || Kind == FeatureModelNodeKind.Root) return true;
            foreach (FeatureNode node in required)  if(!node.IsChecked) return true;
            foreach (FeatureNode node in requiredBy) 
                if ((node.Kind != FeatureModelNodeKind.Alternative && node.IsChecked)
                || (node.Kind == FeatureModelNodeKind.Alternative && node.requiredBy[0] != null && node.requiredBy[0].IsChecked)) 
                    return true;
            foreach (FeatureNode node in excludedBy) if (node.IsChecked) return true;
            foreach (FeatureNode node in excludes) if (node.IsChecked) return true;

            return false;
        }

        private void notifyUI()
        {
            foreach (FeatureNode node in required){
                node.UINode.update();
                if (node.Kind == FeatureModelNodeKind.Alternative
                    && node.required[0] != null)
                {
                    node.required[0].UINode.update();
                }
            }
            foreach (FeatureNode node in requiredBy)
            {
                node.UINode.update();
                if (node.Kind == FeatureModelNodeKind.Alternative
                    && node.required[0] != null)
                {
                    node.required[0].UINode.update();
                }
            }
            foreach (FeatureNode node in excludedBy) node.UINode.update();
            foreach (FeatureNode node in excludes) node.UINode.update();
            UINode.update();
        }
    }
}
